#ifndef OBJC_RUNTIME_H
#define OBJC_RUNTIME_H

#include <sobjc-commons/port.h>
#include <sobjc-commons/list.h>

#ifdef __cplusplus
extern "C" {
#ifdef 0
} /* for editor */
#endif
#endif 

typedef struct objc_selector            *SEL;
typedef struct objc_object              *id;
typedef struct objc_class               *Class;
typedef struct objc_category            *Category;
typedef struct objc_ivar                *Ivar;
typedef struct objc_method              *Method;
typedef struct objc_method_description  *MethodDescription;
typedef struct objc_proto_class         *ProtoClass;
typedef struct objc_inline_cache        *InlineCache;
typedef id (*IMP)(id, SEL, ...);

typedef struct objc_class_list              *ClassList;
typedef struct objc_ivar_list               *IvarList;
typedef struct objc_method_list             *MethodList;
typedef struct objc_protocol_list           *ProtocolList;
typedef struct objc_method_description_list *MethodDescriptionList;
typedef struct objc_category_list           *CategoryList;

struct objc_object {
  Class isa;
};

struct objc_class {
  Class isa; /* compatible with `objc_object' */
  Class superclass;
  const char *name;
  int version;
  unsigned int flags;
  size_t instance_size;
  IvarList ivars;
  MethodList methods;
  ProtocolList protocols;
  ClassList subclasses;
  int dtable_size;
  IMP dtable[1];
};

struct objc_super {
  id recv;
  Class cls;
};

struct objc_inline_cache {
  Class recv_classes[3];
  IMP imps[3];
};

#ifdef __OBJC__
@class Protocol;
#else
typedef struct objc_object Protocol;
#endif /* __OBJC__ */

#ifndef Nil
#define Nil 0   /* id of Nil class */
#endif

#ifndef nil
#define nil 0   /* id of Nil instance */
#endif

OBJC_SIMPLE_DEF_LIST(class_list, Class)
OBJC_SIMPLE_DEF_LIST(ivar_list, Ivar)
OBJC_SIMPLE_DEF_LIST(method_list, Method)
OBJC_SIMPLE_DEF_LIST(protocol_list, Protocol *)
OBJC_SIMPLE_DEF_LIST(method_description_list, MethodDescription)
OBJC_SIMPLE_DEF_LIST(category_list, Category)

typedef Class (*objc_lookup_class_f)(const char *);

OBJC_EXPORT void objc_init_runtime();
OBJC_EXPORT void objc_register_proto_class(ProtoClass);

OBJC_EXPORT ClassList objc_get_class_list();
OBJC_EXPORT Class objc_lookup_class(const char *);
OBJC_EXPORT void objc_set_lookup_class_callback(objc_lookup_class_f);
OBJC_EXPORT void objc_add_class(Class);
OBJC_EXPORT void objc_register_category(Category);
OBJC_EXPORT IMP objc_lookup_imp(id, SEL);
OBJC_EXPORT IMP objc_lookup_imp_super(Class, id, SEL);
OBJC_EXPORT id objc_nil_return(id, SEL, ...);
OBJC_EXPORT id objc_fail_dispatch(id, SEL, ...);

static __inline__ IMP
objc_lookup_imp_inline(id recv, SEL sel, InlineCache cache)
{
#ifndef OBJC_NO_NIL_RECEIVERS
  if (recv != nil) {
#endif
    if (recv->isa == cache->recv_classes[0])
      return cache->imps[0];
    else if (recv->isa == cache->recv_classes[1])
      return cache->imps[1];
    else if (recv->isa == cache->recv_classes[2])
      return cache->imps[2];
    else {
      cache->recv_classes[2] = cache->recv_classes[1];
      cache->recv_classes[1] = cache->recv_classes[0];
      cache->recv_classes[0] = recv->isa;
      cache->imps[2] = cache->imps[1];
      cache->imps[1] = cache->imps[0];
      return cache->imps[0] = objc_lookup_imp(recv, sel);
    }
#ifndef OBJC_NO_NIL_RECEIVERS
  } else
    return objc_nil_return;
#endif
}

static __inline__ IMP
objc_lookup_imp_super_inline(Class superclass,
                             id recv,
                             SEL sel,
                             InlineCache cache)
{
  if (recv->isa == cache->recv_classes[0])
    return cache->imps[0];
  else if (recv->isa == cache->recv_classes[1])
    return cache->imps[1];
  else if (recv->isa == cache->recv_classes[2])
    return cache->imps[2];
  else {
    cache->recv_classes[2] = cache->recv_classes[1];
    cache->recv_classes[1] = cache->recv_classes[0];
    cache->recv_classes[0] = recv->isa;
    cache->imps[2] = cache->imps[1];
    cache->imps[1] = cache->imps[0];
    return cache->imps[0] = objc_lookup_imp_super(superclass, recv, sel);
  }
}

static __inline__ IMP
objc_dispatch_imp(id recv, int code)
{
  IMP imp;
  Class cls;

#ifndef OBJC_NO_NIL_RECEIVERS
  if (recv != nil) {
#endif
    if ((imp = recv->isa->dtable[code]) != NULL)
      return imp;
    else if ((cls = recv->isa->superclass) != NULL) {
      while (cls) {
        if ((imp = cls->dtable[code]) != NULL)
          return imp;
        cls = cls->superclass;
      }
      return objc_fail_dispatch;
    } else
      return objc_fail_dispatch;
#ifndef OBJC_NO_NIL_RECEIVERS
  } else
    return objc_nil_return;
#endif
}

static __inline__ IMP
objc_dispatch_imp_super(Class superclass,
                        id recv __attribute__((unused)),
                        int code)
{
  IMP imp;
  Class cls;

  if ((imp = superclass->dtable[code]) != NULL)
    return imp;
  else if ((cls = superclass->superclass) != NULL) {
    while (cls) {
      if ((imp = cls->dtable[code]) != NULL)
        return imp;
      cls = cls->superclass;
    }
    return objc_fail_dispatch;
  } else
    return objc_fail_dispatch;
}

#ifdef __cplusplus
}
#endif
#endif /* OBJC_RUNTIME_H */

